package com.rajaev.dtu.tractor.app.processor;

import cn.hutool.core.math.MathUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONObject;
import com.rajaev.dtu.tractor.app.service.BatteryElectricService;
import com.rajaev.dtu.tractor.app.service.BatteryService;
import com.rajaev.dtu.tractor.domain.entity.*;
import com.rajaev.dtu.tractor.domain.repository.mongo.DeviceElectricityRepository;
import com.rajaev.dtu.tractor.domain.repository.redis.DeviceMonitorRedisRepository;
import com.rajaev.dtu.tractor.infrastructure.annotations.SNotiCollectionField;
import com.rajaev.dtu.tractor.infrastructure.annotations.SNotiFiled;
import com.rajaev.dtu.tractor.infrastructure.base.enums.BatteryElectricType;
import com.rajaev.dtu.tractor.infrastructure.base.enums.BatteryWorkStateEnum;
import com.rajaev.dtu.tractor.infrastructure.base.enums.SnotiEventType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.xnio.AutomaticReference;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Component
public class DeviceMonitorMessageProcessor {

    private static final String totalA_key = "B_DCZDL";

    @Autowired
    DeviceElectricityRepository deviceElectricityRepository;

    @Autowired
    BatteryService batteryService;

    @Autowired
    BatteryElectricService batteryElectricService;

    @Autowired
    DeviceMonitorRedisRepository deviceMonitorRedisRepository;

    public void doProcessSnotiMonitorMessage(SnotiMessage snotiMessage) {
        SnotiEventType snotiEventType = SnotiEventType.getType(snotiMessage.getEvent_type());
        BatteryPo batteryPo = SnotiMessageContextHolder.getTargetBattery();

        if (snotiEventType.equals(SnotiEventType.KV)) {
            int cellNum = SnotiMessageContextHolder.getTargetBattery().getCellNum();
            int temperatureNum = SnotiMessageContextHolder.getTargetBattery().getTemperatureNum();

            JSONObject jsonObject = snotiMessage.getData();
            Protocol485Dto protocol485Dto = new Protocol485Dto();
            List<Protocol485Dto.BatteryVoltage> voltages = new ArrayList<>();
            List<Protocol485Dto.BatteryTemperature> temperatures = new ArrayList<>();
            protocol485Dto.setBatteryVoltageList(voltages);
            protocol485Dto.setBatteryTemperatureList(temperatures);

            ReflectionUtils.doWithFields(protocol485Dto.getClass(), field -> {
                if (field.isAnnotationPresent(SNotiFiled.class)) {
                    SNotiFiled sNotiFiledDefine = field.getAnnotation(SNotiFiled.class);
                    String fieldId = sNotiFiledDefine.id();
                    Object o = jsonObject.get(fieldId);
                    if (null == o) {
                        return;
                    }
                    field.setAccessible(true);
                    ReflectUtil.setFieldValue(protocol485Dto, field, o);
                } else if (field.isAnnotationPresent(SNotiCollectionField.class)) {
                    SNotiCollectionField sNotiCollectionField = field.getAnnotation(SNotiCollectionField.class);
                    String fieldIdPrefix = sNotiCollectionField.idPrefix();
                    if (fieldIdPrefix.contains("C_DTDY")) {
                        for (int i = 0; i < cellNum; i++) {
                            String key = String.format("%s_%02d", "C_DTDY", i);
                            if (jsonObject.containsKey(key)) {
                                Protocol485Dto.BatteryVoltage batteryVoltage = protocol485Dto.new BatteryVoltage();
                                batteryVoltage.setDtbh(i);
                                batteryVoltage.setDtdy(jsonObject.getBigDecimal(key));
                                voltages.add(batteryVoltage);
                            }

                        }
                    } else if (fieldIdPrefix.contains("C_DTWD")) {
                        for (int i = 0; i < temperatureNum; i++) {
                            String key = String.format("%s_%d", "C_DTWD", i);
                            if (jsonObject.containsKey(key)) {
                                Protocol485Dto.BatteryTemperature batteryTemperature = protocol485Dto.new BatteryTemperature();
                                batteryTemperature.setWdbh(i);
                                batteryTemperature.setDcwd(jsonObject.getInteger(key));
                                temperatures.add(batteryTemperature);
                            }
                        }
                    }
                }
            });
            SnotiMessageContextHolder.setDeviceMonitorInfo(protocol485Dto);

            protocol485Dto.setTickTime(new BigDecimal(snotiMessage.getCreated_at()).longValue());
            deviceMonitorRedisRepository.saveDeviceMonitor(batteryPo, protocol485Dto);
        }
    }

    /**
     * 处理设备的充电和放电情况，生产充放电记录。
     *
     * @param snotiMessage
     */
    public void doProcessDeviceElectricity(SnotiMessage snotiMessage) {
        SnotiEventType snotiEventType = SnotiEventType.getType(snotiMessage.getEvent_type());
        if (SnotiEventType.KV.equals(snotiEventType)) {

            BatteryPo batteryPo = SnotiMessageContextHolder.getTargetBattery();
            BatteryWorkStateEnum batteryWorkStateEnum = batteryService.getBatteryWorkState(batteryPo.getVin());

            if (snotiMessage.getData().containsKey(totalA_key)) {
                BigDecimal totalA = snotiMessage.getData().getBigDecimal(totalA_key);

                if (batteryWorkStateEnum == null) {
                    return;
                }

                switch (batteryWorkStateEnum) {
                    case CHARGE: {  //设备目前在充电状态
                        BatteryElectricLog batteryElectricLog = batteryElectricService.selectChargingLog(batteryPo.getVin());
                        if (batteryElectricLog == null) {
                            //没有正在充电或放电的记录，需要新增一条记录。
                            batteryElectricLog = new BatteryElectricLog(batteryPo.getVin(), BatteryElectricType.CHARGE, new Date(new BigDecimal(snotiMessage.getCreated_at()).longValue() * 1000));
                            batteryElectricService.addBatteryElectricLog(batteryElectricLog);
                            BigDecimal totalV = deviceMonitorRedisRepository.getDeviceMonitor(batteryPo.getVin()).getDczdy();
                            deviceElectricityRepository.saveElectricityRecord(batteryElectricLog.getId(), totalA, totalV, batteryPo.getVin(), new BigDecimal(snotiMessage.getCreated_at()).longValue());
                        }

                        break;
                    }
                    case DISCHARGE: {
                        /**
                         * 1. 判断该设备是否有正在进行中的充电订单，如果不存在，则新建一条。
                         */
                        BatteryElectricLog batteryElectricLog = batteryElectricService.selectDisChargingLog(batteryPo.getVin());
                        if (batteryElectricLog == null) {
                            //没有正在充电的记录，需要新增一条记录。
                            batteryElectricLog = new BatteryElectricLog(batteryPo.getVin(), BatteryElectricType.DISCHARGE, new Date(new BigDecimal(snotiMessage.getCreated_at()).longValue() * 1000));
                            batteryElectricService.addBatteryElectricLog(batteryElectricLog);
                            totalA = snotiMessage.getData().getBigDecimal(totalA_key);
                            BigDecimal totalV = deviceMonitorRedisRepository.getDeviceMonitor(batteryPo.getVin()).getDczdy();
                            deviceElectricityRepository.saveElectricityRecord(batteryElectricLog.getId(), totalA, totalV, batteryPo.getVin(), new BigDecimal(snotiMessage.getCreated_at()).longValue());
                        }
                        break;
                    }
                    default: {
                        BatteryElectricLog batteryElectricLog = batteryElectricService.selectNotOveredLog(batteryPo.getVin());
                        if (batteryElectricLog == null) {
                            break;
                        }
                        List<ElectricityDto> list = deviceElectricityRepository.selectElectricRecordByOrderId(batteryElectricLog.getId());
                        switch (batteryElectricLog.getType()) {
                            case CHARGE: {
                                //计算电量
                                BigDecimal totalElectricity = calculateTotalElectric(list);
                                batteryElectricLog.setElectricity(totalElectricity.doubleValue());
                                batteryElectricLog.setEndTime(new Date(new  BigDecimal(snotiMessage.getCreated_at()).longValue()*1000));
                                batteryElectricService.updateBatteryElectricLog(batteryElectricLog);
                            }
                            case DISCHARGE: {
                                BigDecimal totalElectricity = calculateTotalElectric(list);
                                batteryElectricLog.setElectricity(totalElectricity.doubleValue());
                                batteryElectricLog.setEndTime(new Date(new  BigDecimal(snotiMessage.getCreated_at()).longValue()*1000));
                                batteryElectricService.updateBatteryElectricLog(batteryElectricLog);
                            }
                            default:
                                break;
                        }
                    }
                }
            }
        }
    }

    public BigDecimal calculateTotalElectric(List<ElectricityDto> list){
        ElectricityDto standardElectricDto = null;
        BigDecimal totalElectricity = BigDecimal.ZERO;
        for (ElectricityDto electricityDto : list) {
            if (standardElectricDto==null){
                standardElectricDto = electricityDto;
            }
            double avgA = (electricityDto.getTotalA() + standardElectricDto.getTotalA())/2;
            double avgV = (electricityDto.getTotalV() + standardElectricDto.getTotalV())/2;
            long diffSeconds = electricityDto.getCreateAt() - standardElectricDto.getCreateAt();
            //当前片段的电量
            BigDecimal segmentElectricity = BigDecimal.valueOf(avgA).multiply(BigDecimal.valueOf(avgV)).multiply(BigDecimal.valueOf(diffSeconds)).divide(BigDecimal.valueOf(3600),10,BigDecimal.ROUND_DOWN);
            totalElectricity = totalElectricity.add(segmentElectricity);
        }
        return totalElectricity;
    }

}